
#include "stdafx.h"
#include <JeayeSDK/JJsonW.h>
#include <JeayeSDK/CharFun.h>
#include <wchar.h>
#include <math.h>

#ifdef _DEBUG
#define new new(__FILE__, __LINE__)
#endif

JJsonW::JJsonW()
{
}

JJsonW::~JJsonW()
{
	destroy();
}

BOOL JJsonW::parse_json(const wchar_t * data)
{
	_ASSERT(data != NULL);
	if (data == NULL)
		return FALSE;

	json_read(myStrTrait::skip_s(data));

	return TRUE;
}

const wchar_t* JJsonW::json_read(const wchar_t *s)
{
	stStringBlockInfoW block = { 0 };

	json_type = C_JSON_NULL;
	/* Build an object from input text. */
	if (OBJECT_START(*s)) {
		json_type = C_JSON_OBJECT;
		object.c_object = hmap_create(TABLE_INIT_SIZE, NULL, NULL);

		s = myStrTrait::skip_s(++s);
		if (s) {
			/* empty object. */
			if (OBJECT_END(*s)) {
				return myStrTrait::skip_s(++s);
			}

			do {
				s = read_key_and_value(s);
				if (s && NEXT_VALUE(*s)) {
					s = myStrTrait::skip_s(++s);
				}
				else {
					break;
				}
			} while (s);

			if (s && OBJECT_END(*s)) {
				return myStrTrait::skip_s(s + 1);
			}
		}

		return NULL;
	}

	/* Build an array from input text. */
	if (ARRAY_START(*s)) {
		json_type = C_JSON_ARRAY;
		object.c_array = new JArray(ARRAY_INIT_SIZE);

		s = myStrTrait::skip_s(++s);
		if (s) {
			/* empty array. */
			if (ARRAY_END(*s)) {
				return myStrTrait::skip_s(++s);
			}

			do {
				s = read_array_value(s);
				if (s && NEXT_VALUE(*s)) {
					s = myStrTrait::skip_s(++s);
				}
				else {
					break;
				}
			} while (s);

			if (s && ARRAY_END(*s)) {
				return myStrTrait::skip_s(s + 1);
			}
		}

		return NULL;
	}

	/*string*/
	s = json_read_string(s, 0, &block);
	if (block.finded) {
		wchar_t *cpys = NULL;
		json_type = C_JSON_STRING;
		object.c_string = C_MALLOCX(wchar_t, (block.count + 1));
		cpys = object.c_string;
		while (block.start_str != block.end_str) {
			if (*block.start_str != '\\')
				*cpys++ = *block.start_str++;
			else {
				block.start_str++;
				switch (*block.start_str)
				{
				case _T('b'): *cpys++ = _T('\b'); block.start_str++; break;
				case _T('f'): *cpys++ = _T('\f'); block.start_str++; break;
				case _T('n'): *cpys++ = _T('\n'); block.start_str++; break;
				case _T('r'): *cpys++ = _T('\r'); block.start_str++; break;
				case _T('t'): *cpys++ = _T('\t'); block.start_str++; break;
				case _T('\"'):
				case _T('\\'):
				case _T('/'): *cpys++ = *block.start_str++; break;
				default:  *cpys++ = _T('\\'); *cpys++ = *block.start_str++; break;
				}
			}
		}
		*cpys = 0;
		//
		return s;
	}

	if (*s == _T('-') || _istdigit(*s)) {
		return myTrial::read_number(s,this);
	}

	if (_wcsnicmp(s, L"true", 4) == 0) {
		json_type = C_JSON_BOOLEAN;
		object.c_boolean = TRUE;
		s += 4;
	}
	else if (_wcsnicmp(s, L"false", 5) == 0) {
		json_type = C_JSON_BOOLEAN;
		object.c_boolean = FALSE;
		s += 5;
	}
	else if (_wcsnicmp(s, L"null", 4) == 0) {
		json_type = C_JSON_NULL;
		object.c_boolean = FALSE;
		s += 4;
	}

	return myStrTrait::skip_s(s);
}

const wchar_t* JJsonW::json_read_string(const wchar_t *s, wchar_t end_flag, stStringBlockInfoW *block)
{
	if (block == NULL) {
		return NULL;
	}

	block->finded = FALSE;
	block->count = 0;
	block->start_str = NULL;
	block->end_str = NULL;
	if (*s == _T('\'')) {
		block->finded = TRUE;
		block->start_str = ++s;
		while (s) {
			if (*(s - 1) != _T('\\') && *s == _T('\'')) {
				block->end_str = s;
				s++;
				break;
			}
			block->end_str = ++s;
		}
	}
	else if (*s == _T('"')) {
		block->finded = TRUE;
		block->start_str = ++s;
		while (s) {
			if (*(s - 1) != _T('\\') && *s == _T('"')) {
				block->end_str = s;
				s++;
				break;
			}
			block->end_str = ++s;
		}
	}
	else if (end_flag) {
		block->finded = TRUE;
		block->start_str = s;
		while (s && !c_is_space(static_cast<char>(*s)))
		{
			if (*s == end_flag) {
				block->end_str = s;
				break;
			}
			block->end_str = ++s;
		}
	}

	if (block->end_str) {
		block->count = (int)(block->end_str - block->start_str);
	}

	return myStrTrait::skip_s(s);
}

const wchar_t* JJsonW::read_key_and_value(const wchar_t *s)
{
	JVariant var;
	stStringBlockInfoW block = { 0 };
	JJsonW *new_json = NULL;

	s = json_read_string(s, _T(':'), &block);
	if (s == NULL) {
		return NULL;
	}
	else if (*s != _T(':')) {
		return NULL;
	}

	new_json = C_CALLOC(JJsonW, 1);
	if (new_json == NULL)
		return NULL;

	if (block.finded) {
		new_json->key_value = C_MALLOCX(wchar_t, (block.count + 1));
		wcscpy_s(new_json->key_value, block.count,block.start_str);
		new_json->key_value[block.count] = L'\0';
	}
	/* skip++ ':' */
	s = new_json->json_read(myStrTrait::skip_s(++s));

	var.set(new_json->key_value);
	if (hmap_insert(object.c_object, var.get_variant(), new_json) == FALSE) {
		new_json->destroy();
		c_free(new_json);
		return NULL;
	}

	return s;
}

JJsonW * JJsonW::create_json_object(BYTE json_type, const wchar_t * key_value)
{
	JJsonW *j = (JJsonW*)c_malloc(sizeof(JJsonW));
	if (j == NULL) {
		return NULL;
	}

	ZeroMemory(j, sizeof(JJsonW));

	j->json_type = json_type;
	if (j->json_type == C_JSON_OBJECT) {
		j->object.c_object = hmap_create(TABLE_INIT_SIZE, NULL, NULL);
		if (j->object.c_object == NULL) {
			c_free(j);
			return NULL;
		}
	}
	else if (j->json_type == C_JSON_ARRAY) {
		j->object.c_array = new JArray(ARRAY_INIT_SIZE);
		if (j->object.c_array == NULL) {
			c_free(j);
			return NULL;
		}
	}

	if (key_value)
		j->key_value = c_wcsdup(key_value);
	else
		j->key_value = NULL;

	return j;
}

JJsonW * JJsonW::create_json_bool(BYTE json_type, const wchar_t * key_value, BOOL value_data)
{
	JJsonW *j = create_json_object(json_type, key_value);
	if (j == NULL) return NULL;
	j->object.c_boolean = value_data;
	return j;
}

JJsonW * JJsonW::create_json_number(BYTE json_type, const wchar_t * key_value, INT64 value_data)
{
	JJsonW *j = create_json_object(json_type, key_value);
	if (j == NULL) return NULL;
	j->object.c_int = value_data;
	return j;
}

JJsonW * JJsonW::create_json_double(BYTE json_type, const wchar_t * key_value, double value_data)
{
	JJsonW *j = create_json_object(json_type, key_value);
	if (j == NULL) return NULL;
	j->object.c_double = value_data;
	return j;
}

JJsonW * JJsonW::create_json_string(const wchar_t * key_value, const wchar_t * value_data)
{
	JJsonW *j = create_json_object(C_JSON_STRING, key_value);
	if (j == NULL) return NULL;
	j->object.c_string = c_wcsdup(value_data);
	return j;
}

BOOL JJsonW::append_json_object(JJsonW * j)
{
	JVariant var;
	if (j == NULL) {
		return FALSE;
	}

	if (json_type == C_JSON_OBJECT) 
	{
		var.set(j->key_value);
		if (hmap_insert(object.c_object, var.get_variant(), j) == FALSE) {
			j->destroy();
			c_free(j);
			return FALSE;
		}
	}
	else if (json_type == C_JSON_ARRAY) {
		object.c_array->push(j);
	}
	return TRUE;
}

JJsonW* JJsonW::FindObject(const wchar_t *key_name)
{
	JVariant var;

	if (json_type != C_JSON_OBJECT) {
		return NULL;
	}
	var.set(key_name);
	return (JJsonW*)hmap_find(object.c_object, var.get_variant());
}

_c_hmap * JJsonW::object_find_object(const wchar_t * key_name)
{
	JJsonW * finded = FindObject(key_name);
	if (!finded || finded->json_type != C_JSON_OBJECT) {
		return NULL;
	}
	return finded->object.c_object;
}


BOOL JJsonW::object_find_boolean(const wchar_t *key_name)
{
	JJsonW * finded = FindObject(key_name);
	if (!finded || finded->json_type != C_JSON_BOOLEAN) {
		return FALSE;
	}
	return finded->object.c_boolean;
}

INT64 JJsonW::object_find_number(const wchar_t *key_name)
{
	JJsonW * finded = FindObject(key_name);
	if (finded && finded->json_type == C_JSON_INT) {
		return finded->object.c_int;
	}
	return 0;
}
double JJsonW::object_find_double(const wchar_t *key_name)
{
	JJsonW * finded = FindObject(key_name);
	if (finded && finded->json_type == C_JSON_DOUBLE) {
		return finded->object.c_double;
	}
	return 0;
}
wchar_t* JJsonW::object_find_string(const wchar_t *key_name)
{
	JJsonW * finded = FindObject(key_name);
	if (finded && finded->json_type == C_JSON_STRING) {
		return finded->object.c_string;
	}
	return NULL;
}
JArray* JJsonW::object_find_array(const wchar_t *key_name)
{
	JJsonW * finded = FindObject( key_name);
	if (finded && finded->json_type == C_JSON_ARRAY) {
		return finded->object.c_array;
	}
	return NULL;
}
JJsonW* JJsonW::array_find(UINT index)
{
	JJsonW *json_obj = NULL;
	if (json_type == C_JSON_ARRAY) {
		return json_obj = (JJsonW*)object.c_array->find(index);
	}
	return NULL;
}

void JJsonW::save_json_file(FILE *fp)
{
	if (fp == NULL) {
		return;
	}
	save_json_value_file(fp);
}

BOOL JJsonW::destroy()
{
	if (destroy_json_object()) {
		return TRUE;
	}

	if (destroy_json_array()) {
		return TRUE;
	}

	return FALSE;
}
BOOL JJsonW::destroy_json_object()
{
	if (json_type == C_JSON_OBJECT)
	{
		double_iterator* __NEXT = ((double_link*)hmap_link(object.c_object))->first();
		JJsonW *obj = NULL;
		c_variant_t *key = NULL;
		while (NULL != __NEXT)
		{
			obj = (JJsonW*)hmap_value(__NEXT, &key);
			__NEXT = __NEXT->m_next;
			switch (obj->json_type) {
			case C_JSON_OBJECT:
				obj->destroy_json_object();
				break;
			case C_JSON_STRING:
				c_free(obj->object.c_string);
				break;
			case C_JSON_ARRAY:
				obj->destroy_json_array();
				break;
			}

			if (obj->key_value) {
				c_free(obj->key_value);
			}
			c_free(obj);
		}

		hmap_destroy(&object.c_object);
		return TRUE;
	}
	return FALSE;
}

BOOL JJsonW::destroy_json_array()
{
	if (json_type == C_JSON_ARRAY)
	{
		JJsonW* obj = NULL;
		UINT32 __COUNT = object.c_array->get_count(), key = 0;
		for (key = 0; key < __COUNT; key++)
		{
			obj = (JJsonW*)object.c_array->find(key);
			switch (obj->json_type) {
			case C_JSON_OBJECT:
				obj->destroy_json_object();
				break;
			case C_JSON_STRING:
				c_free(obj->object.c_string);
				break;
			case C_JSON_ARRAY:
				obj->destroy_json_array();
				break;
			}
			c_free(obj);
		}

		delete object.c_array;		object.c_array = NULL;	json_type = C_JSON_NULL;
		return TRUE;
	}

	return FALSE;
}

void JJsonW::save_json_value_file(FILE * fp)
{
	switch (this->json_type)
	{
	case C_JSON_OBJECT: {
		save_json_object_file(fp);
	}
						break;
	case C_JSON_BOOLEAN: {
		_ftprintf(fp, _T("%s"), this->object.c_boolean == TRUE ? _T("true") : _T("false"));
		break;
	}
	case C_JSON_DOUBLE: {
		_ftprintf(fp, _T("%f"), this->object.c_double);
		break;
	}
	case C_JSON_STRING: {
		if (this->object.c_string == NULL)
			_fputts(_T("\"\""), fp);
		else
			_ftprintf(fp, _T("\"%s\""), this->object.c_string);
		break;
	}
	case C_JSON_INT: {
		_ftprintf(fp, _T("%lld"), (long long)this->object.c_int);
		break;
	}
	case C_JSON_ARRAY: {
		//_tprintf(_T("\n--object print(array)--\n"));
		this->save_json_array_file(fp);
		break;
	}
	case C_JSON_NULL: {
		_fputts(_T("null"), fp);
		break;
	}
	default:
		//_tprintf(_T("error type %d\n"), js_obj->json_type);
		break;
	}
}

void JJsonW::save_json_array_file(FILE * fp)
{
	UINT count = this->object.c_array->get_count() - 1;
	_fputtc(_T('['), fp);
	JJsonW* js_obj = NULL;
	UINT32 __COUNT = this->object.c_array->get_count(), key = 0;
	for (key = 0; key < __COUNT; key++)
	{
		js_obj = (JJsonW*)this->object.c_array->find(key);
		if (js_obj == NULL) continue;
		js_obj->save_json_value_file(fp);
		if (key < count)
			_fputtc(_T(','), fp);
	}
	_fputtc(_T(']'), fp);
}

void JJsonW::save_json_object_file(FILE * fp)
{
	double_iterator* _next = ((double_link*)hmap_link(this->object.c_object))->first();
	JJsonW *js_obj = NULL;
	c_variant_t *key = NULL;

	_fputtc(_T('{'), fp);
	while (NULL != _next) {
		js_obj = (JJsonW*)hmap_value(_next, &key);
		_next = _next->m_next;

		if (js_obj == NULL) continue;
		if (js_obj->key_value) {
			_ftprintf(fp, _T("\"%s\":"), js_obj->key_value);
		}

		this->save_json_value_file(fp);

		if (_next != NULL) {
			_fputtc(_T(','), fp);
		}
	}
	_fputtc(_T('}'), fp);
}

